{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "25ecbde0",
   "metadata": {},
   "source": [
    "# 使用 LangChain 搭建 Agent 示例\n",
    "搭建一个配备了一个通用搜索工具，并且具备对话记忆功能的多轮聊天机器人。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "517f1fd2",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from dotenv import load_dotenv\n",
    "\n",
    "load_dotenv()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7a3d0c02",
   "metadata": {},
   "source": [
    "## 定义工具\n",
    "使用 Tavily——一个搜索引擎工具。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "bef41ee0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'query': 'What is the weather in BeiJing', 'follow_up_questions': None, 'answer': None, 'images': [], 'results': [{'title': 'Weather in Beijing', 'url': 'https://www.weatherapi.com/', 'content': \"{'location': {'name': 'Beijing', 'region': 'Beijing', 'country': 'China', 'lat': 39.9289, 'lon': 116.3883, 'tz_id': 'Asia/Shanghai', 'localtime_epoch': 1752741693, 'localtime': '2025-07-17 16:41'}, 'current': {'last_updated_epoch': 1752741000, 'last_updated': '2025-07-17 16:30', 'temp_c': 35.2, 'temp_f': 95.4, 'is_day': 1, 'condition': {'text': 'Partly Cloudy', 'icon': '//cdn.weatherapi.com/weather/64x64/day/116.png', 'code': 1003}, 'wind_mph': 2.2, 'wind_kph': 3.6, 'wind_degree': 12, 'wind_dir': 'NNE', 'pressure_mb': 1002.0, 'pressure_in': 29.59, 'precip_mm': 0.0, 'precip_in': 0.0, 'humidity': 42, 'cloud': 0, 'feelslike_c': 33.4, 'feelslike_f': 92.2, 'windchill_c': 38.2, 'windchill_f': 100.7, 'heatindex_c': 37.3, 'heatindex_f': 99.1, 'dewpoint_c': 10.9, 'dewpoint_f': 51.5, 'vis_km': 10.0, 'vis_miles': 6.0, 'uv': 3.5, 'gust_mph': 6.7, 'gust_kph': 10.8}}\", 'score': 0.9605225, 'raw_content': None}, {'url': 'https://world-weather.info/forecast/china/beijing/july-2025/', 'title': 'Weather in Beijing in July 2025', 'content': 'Detailed ⚡ Beijing Weather Forecast for July 2025 – day/night 🌡️ ... Thursday, 17 July. +77°. Day. +90°. Clear sky. Friday, 18 July. +79°. Day. +90°. Partly', 'score': 0.91760606, 'raw_content': None}], 'response_time': 2.05}\n"
     ]
    }
   ],
   "source": [
    "from langchain_tavily import TavilySearch\n",
    "\n",
    "search = TavilySearch(max_results=2)\n",
    "search_results = search.invoke(\"What is the weather in BeiJing\")\n",
    "print(search_results)\n",
    "# If we want, we can create other tools.\n",
    "# Once we have all the tools we want, we can put them in a list that we will reference later.\n",
    "tools = [search]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f88e8f4c",
   "metadata": {},
   "source": [
    "## 使用大语言模型\n",
    "这里使用Google的gemini"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "f2cb3c66",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Hello! How can I assist you today? 😊'"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.chat_models import init_chat_model\n",
    "\n",
    "model = init_chat_model(\n",
    "    \"qwen-plus\",\n",
    "    model_provider=\"openai\",\n",
    "    base_url=\"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n",
    ")\n",
    "\n",
    "query = \"Hi!\"\n",
    "response = model.invoke([{\"role\": \"user\", \"content\": query}])\n",
    "response.text()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5a3de6e9",
   "metadata": {},
   "source": [
    "下面尝试加入工具，再次进行大模型调用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2126ceb4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Message content: Hello! How can I assist you today?\n",
      "\n",
      "Tool calls: []\n"
     ]
    }
   ],
   "source": [
    "model_with_tools = model.bind_tools(tools)\n",
    "\n",
    "query = \"Hi!\"\n",
    "response = model_with_tools.invoke([{\"role\": \"user\", \"content\": query}])\n",
    "\n",
    "print(f\"Message content: {response.text()}\\n\")\n",
    "print(f\"Tool calls: {response.tool_calls}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "90b651b3",
   "metadata": {},
   "source": [
    "调整输入，使得工具可以调用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "08968dc7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Message content: \n",
      "\n",
      "Tool calls: [{'name': 'tavily_search', 'args': {'query': 'weather in San Francisco'}, 'id': 'call_607daf7695964832bdc92a', 'type': 'tool_call'}]\n"
     ]
    }
   ],
   "source": [
    "query = \"Search for the weather in SF\"\n",
    "response = model_with_tools.invoke([{\"role\": \"user\", \"content\": query}])\n",
    "\n",
    "print(f\"Message content: {response.text()}\\n\")\n",
    "print(f\"Tool calls: {response.tool_calls}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b4833303",
   "metadata": {},
   "source": [
    "## 创建智能体\n",
    "\n",
    "借助 LangGraph，使用大语言模型（LLM）和工具来初始化智能体。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "17cfa181",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "Hi!\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "Hello! How can I assist you today?\n"
     ]
    }
   ],
   "source": [
    "from langgraph.prebuilt import create_react_agent\n",
    "\n",
    "agent_executor = create_react_agent(model, tools)\n",
    "\n",
    "# 运行智能体\n",
    "input_message = {\"role\": \"user\", \"content\": \"Hi!\"}\n",
    "response = agent_executor.invoke({\"messages\": [input_message]})\n",
    "\n",
    "for message in response[\"messages\"]:\n",
    "    message.pretty_print()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "2da7c7fb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "Search for the weather in SF\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "Tool Calls:\n",
      "  tavily_search (call_d2040a04766e476bb30639)\n",
      " Call ID: call_d2040a04766e476bb30639\n",
      "  Args:\n",
      "    query: weather in SF\n",
      "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
      "Name: tavily_search\n",
      "\n",
      "{\"query\": \"weather in SF\", \"follow_up_questions\": null, \"answer\": null, \"images\": [], \"results\": [{\"title\": \"Weather in San Francisco\", \"url\": \"https://www.weatherapi.com/\", \"content\": \"{'location': {'name': 'San Francisco', 'region': 'California', 'country': 'United States of America', 'lat': 37.775, 'lon': -122.4183, 'tz_id': 'America/Los_Angeles', 'localtime_epoch': 1752744589, 'localtime': '2025-07-17 02:29'}, 'current': {'last_updated_epoch': 1752743700, 'last_updated': '2025-07-17 02:15', 'temp_c': 15.3, 'temp_f': 59.5, 'is_day': 0, 'condition': {'text': 'Overcast', 'icon': '//cdn.weatherapi.com/weather/64x64/night/122.png', 'code': 1009}, 'wind_mph': 7.4, 'wind_kph': 11.9, 'wind_degree': 251, 'wind_dir': 'WSW', 'pressure_mb': 1014.0, 'pressure_in': 29.94, 'precip_mm': 0.0, 'precip_in': 0.0, 'humidity': 83, 'cloud': 100, 'feelslike_c': 15.3, 'feelslike_f': 59.5, 'windchill_c': 13.4, 'windchill_f': 56.1, 'heatindex_c': 14.0, 'heatindex_f': 57.3, 'dewpoint_c': 13.3, 'dewpoint_f': 56.0, 'vis_km': 16.0, 'vis_miles': 9.0, 'uv': 0.0, 'gust_mph': 10.8, 'gust_kph': 17.4}}\", \"score\": 0.7883153, \"raw_content\": null}, {\"url\": \"https://www.weather25.com/north-america/usa/california/san-francisco?page=month&month=July\", \"title\": \"San Francisco weather in July 2025 | Weather25.com\", \"content\": \"| February | 16° / 7° | 4 | 24 | 0 | 61 mm | Good | San Francisco in February | | March | 17° / 8° | 5 | 26 | 0 | 62 mm | Good | San Francisco in March | | May | 20° / 10° | 1 | 30 | 0 | 13 mm | Good | San Francisco in May | | July | 25° / 14° | 0 | 31 | 0 | 3 mm | Perfect | San Francisco in July | | December | 14° / 8° | 4 | 27 | 0 | 55 mm | Good | San Francisco in December |\", \"score\": 0.78493005, \"raw_content\": null}], \"response_time\": 1.47}\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "The current weather in San Francisco is overcast with a temperature of 15.3°C (59.5°F). The wind is blowing from the WSW at 7.4 mph (11.9 kph), and the humidity is at 83%. The conditions are quite pleasant, with no precipitation recorded.\n",
      "\n",
      "For more details or updates, you can check the [Weather API](https://www.weatherapi.com/) or [Weather25](https://www.weather25.com/north-america/usa/california/san-francisco?page=month&month=July).\n"
     ]
    }
   ],
   "source": [
    "input_message = {\"role\": \"user\", \"content\": \"Search for the weather in SF\"}\n",
    "response = agent_executor.invoke({\"messages\": [input_message]})\n",
    "\n",
    "for message in response[\"messages\"]:\n",
    "    message.pretty_print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7b000235",
   "metadata": {},
   "source": [
    "## 流式消息"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "ec4b688c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "Search for the weather in SF\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "Tool Calls:\n",
      "  tavily_search (call_1076e495bc4e48e29f01b5)\n",
      " Call ID: call_1076e495bc4e48e29f01b5\n",
      "  Args:\n",
      "    query: weather in San Francisco\n",
      "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
      "Name: tavily_search\n",
      "\n",
      "{\"query\": \"weather in San Francisco\", \"follow_up_questions\": null, \"answer\": null, \"images\": [], \"results\": [{\"title\": \"Weather in San Francisco\", \"url\": \"https://www.weatherapi.com/\", \"content\": \"{'location': {'name': 'San Francisco', 'region': 'California', 'country': 'United States of America', 'lat': 37.775, 'lon': -122.4183, 'tz_id': 'America/Los_Angeles', 'localtime_epoch': 1752744589, 'localtime': '2025-07-17 02:29'}, 'current': {'last_updated_epoch': 1752743700, 'last_updated': '2025-07-17 02:15', 'temp_c': 15.3, 'temp_f': 59.5, 'is_day': 0, 'condition': {'text': 'Overcast', 'icon': '//cdn.weatherapi.com/weather/64x64/night/122.png', 'code': 1009}, 'wind_mph': 7.4, 'wind_kph': 11.9, 'wind_degree': 251, 'wind_dir': 'WSW', 'pressure_mb': 1014.0, 'pressure_in': 29.94, 'precip_mm': 0.0, 'precip_in': 0.0, 'humidity': 83, 'cloud': 100, 'feelslike_c': 15.3, 'feelslike_f': 59.5, 'windchill_c': 13.4, 'windchill_f': 56.1, 'heatindex_c': 14.0, 'heatindex_f': 57.3, 'dewpoint_c': 13.3, 'dewpoint_f': 56.0, 'vis_km': 16.0, 'vis_miles': 9.0, 'uv': 0.0, 'gust_mph': 10.8, 'gust_kph': 17.4}}\", \"score\": 0.9177103, \"raw_content\": null}, {\"url\": \"https://world-weather.info/forecast/usa/san_francisco/july-2025/\", \"title\": \"Weather in San Francisco in July 2025 (California)\", \"content\": \"*   1 +70° +59° *   2 +72° +61° *   3 +70° +61° *   4 +70° +59° *   5 +70° +59° *   6 +70° +59° *   7 +70° +59° *   8 +70° +59° *   9 +70° +59° *   10 +72° +61° *   11 +72° +61° *   12 +70° +61° *   13 +70° +59° *   14 +70° +61° *   15 +72° +59° *   16 +72° +59° *   17 +70° +61° *   18 +72° +61° *   19 +70° +61° *   20 +72° +61° *   21 +70° +61° *   22 +72° +61° *   23 +72° +61° *   24 +70° +59° *   25 +72° +61° *   26 +72° +61° *   27 +70° +59° *   28 +70° +59° *   29 +70° +59° *   30 +70° +59°\", \"score\": 0.8616433, \"raw_content\": null}], \"response_time\": 1.44}\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "The current weather in San Francisco is overcast with a temperature of 15.3°C (59.5°F). The wind is blowing at 7.4 mph (11.9 kph) from the west-southwest, and the humidity is at 83%. The conditions are not windy enough for significant wind chill, and there is no precipitation at the moment.\n",
      "\n",
      "For more detailed information, you can check the [WeatherAPI page](https://www.weatherapi.com/).\n"
     ]
    }
   ],
   "source": [
    "for step in agent_executor.stream({\"messages\": [input_message]}, stream_mode=\"values\"):\n",
    "    step[\"messages\"][-1].pretty_print()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "b567fe7e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The| current| weather| in| San Francisco is over|cast with a temperature| of 15|.3°C (|59.5|°F). The wind| is blowing at |7.4 mph| (11.|9 kph)| from the WSW|, and the humidity| is at 8|3%. The overall| conditions appear to be| relatively comfortable.\n",
      "\n",
      "For| more detailed or real|-time updates, you| can visit [Weather|API](https://|www.weatherapi.com|/) or [Weather|25](https|://www.weather2|5.com/north|-america/|usa/california/s|an-francisco|?page=month&|month=July).|"
     ]
    }
   ],
   "source": [
    "for step, metadata in agent_executor.stream(\n",
    "    {\"messages\": [input_message]}, stream_mode=\"messages\"\n",
    "):\n",
    "    if metadata[\"langgraph_node\"] == \"agent\" and (text := step.text()):\n",
    "        print(text, end=\"|\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9e2dee7a",
   "metadata": {},
   "source": [
    "## 添加记忆功能"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "592aaa10",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "Hi, I'm Bob!\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "Hello, Bob! How can I assist you today?\n"
     ]
    }
   ],
   "source": [
    "from langgraph.checkpoint.memory import MemorySaver\n",
    "\n",
    "memory = MemorySaver()\n",
    "\n",
    "agent_executor = create_react_agent(model, tools, checkpointer=memory)\n",
    "\n",
    "# 配置 thread_id，同一个 thread_id 会保存历史会话\n",
    "config = {\"configurable\": {\"thread_id\": \"abc123\"}}\n",
    "\n",
    "input_message = {\"role\": \"user\", \"content\": \"Hi, I'm Bob!\"}\n",
    "for step in agent_executor.stream(\n",
    "    {\"messages\": [input_message]}, config, stream_mode=\"values\"\n",
    "):\n",
    "    step[\"messages\"][-1].pretty_print()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "a1668f13",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "What's my name?\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "Your name is Bob!\n"
     ]
    }
   ],
   "source": [
    "input_message = {\"role\": \"user\", \"content\": \"What's my name?\"}\n",
    "for step in agent_executor.stream(\n",
    "    {\"messages\": [input_message]}, config, stream_mode=\"values\"\n",
    "):\n",
    "    step[\"messages\"][-1].pretty_print()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
